#! /usr/bin/env python
# -*- coding: utf-8 -*-
#
# Copyright (c) 2011 BidiX (http://bidix.info)
#
#   Licensed under the Apache License, Version 2.0 (the "License");
#   you may not use this file except in compliance with the License.
#   You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
#   Unless required by applicable law or agreed to in writing, software
#   distributed under the License is distributed on an "AS IS" BASIS,
#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
#   See the License for the specific language governing permissions and
#   limitations under the License.
#
'''
	gae_client - HTTP Client to Google App Engine
	
	Description :
		- Issue request to a GAE app
		- Login to service if it is requested
		- Manage authentication token in a cookie file 
	
 
	Usage :
		client = GAE_Client(email, password, domain, gae_app, cookies_filename, local=False)
		(response_code, response_body) = client.request(method, url, data=None)
	 
'''
import sys
import cookielib
from urllib import urlencode
import urllib2
import re




class RequestWithMethod(urllib2.Request):
	"""
		Hack for forcing the method in a request – allows PUT and DELETE
		http://benjamin.smedbergs.us/blog/2008-10-21/putting-and-deleteing-in-python-urllib2/
	"""
	def __init__(self, method, *args, **kwargs):
		self._method = method
		urllib2.Request.__init__(self, *args, **kwargs)

	def get_method(self):
		if self._method:
			return self._method
		elif self.has_data():
			return "POST"
		else:
			return "GET"


	
class GAE_Client():
	'''
		provides :
			login(self, target_uri)
			request(self, method, url, data=None
	'''
	token_host = 'www.google.com'
	token_uri = 'https://www.google.com/accounts/ClientLogin'
	login_uri = 'http://%s.appspot.com/_ah/login?%s'

	remote_login_regex = re.compile('id=\"gaia_loginform\"')
	remote_host = 'http://%s.appspot.com:80'
	local_login_regex = re.compile('<form method=\"get\" action=\"/_ah/login\"')
	local_host = 'http://localhost:8080'

	def __init__(self, email, password, source, gae_app, cookies_filename, local=False):
		'''
			Try to load the cookies_filename
			Install an HTTPCookieProcessor 
		'''
		self.email = email
		self.password = password
		self.source = source
		self.gae_app = gae_app
		self.cookiejar = cookielib.LWPCookieJar()
		self.cookiejar.filename = cookies_filename
		self.local = local
		if self.local:
			self.request_host = self.local_host
			self.login_regex = self.local_login_regex
		else:
			self.request_host = self.remote_host%self.gae_app
			self.login_regex = self.remote_login_regex
		try:
			self.cookiejar.load()
		except:
			pass 
		self.opener = urllib2.build_opener(urllib2.HTTPCookieProcessor(self.cookiejar))
		urllib2.install_opener(self.opener)
		
	def __del__(self):
		self.cookiejar.save()

	def login(self, target_uri):
		if not self.local:
			authreq_data = urlencode({'Email': self.email,
											 'Passwd': self.password,
											 'service': 'ah',
											 'source': self.source,
											 'accountType': 'HOSTED_OR_GOOGLE'})
			auth_req = urllib2.Request(self.token_uri, data=authreq_data)
			auth_resp = self.opener.open(auth_req)
			auth_resp_body = auth_resp.read()
			auth_resp_dict = dict(x.split('=')
								 for x in auth_resp_body.split('\n') if x)
			authtoken = auth_resp_dict['Auth']
			authreq_data = urlencode({
											 'continue': target_uri,
											 'auth': authtoken})
			login_uri = self.login_uri % (self.gae_app, authreq_data)
		else:
			authreq_data = urlencode({'email': self.email,
											 'continue': target_uri,
											 'admin': True,
											 'action': 'Login'})
			login_uri = ('%s/_ah/login?%s' % (self.local_host, authreq_data))

		serv_req = urllib2.Request(login_uri)
		response = self.opener.open(serv_req)
		self.cookiejar.save()
		return response
	
	def request(self, method, url, data=None):
		uri = self.request_host + url
		req = RequestWithMethod(method, uri, data)
		response = None
		try:
			response = self.opener.open(req, data)
			response_body = response.read()
		except urllib2.HTTPError, error:
			if error.code != 302 and error.code != 401:
				raise urllib2.HTTPError(uri, error.code, error.msg, None, None)
		if not response or self.login_regex.search(response_body):
			# it's a login page
			if method == 'POST':
				response = self.login('/')
				response_code, response_body = self.request(method, url, data)
				return (response_code, response_body)
			else:
				response = self.login(url)
				response_body = response.read()
				return (response.code, response_body)
		else:
			return (response.code, response_body)

def main(argv):
	'''
		usage : gae_client.py {email} {password}
	'''
	app = GAE_Client(argv[1], argv[2], 'bidix.info', 'notewikiapp', 'cookies2', local=False)
	code, body = app.request('GET', '/_admin.txt', None)
	print "return code : %s"%code
	print body
	
if __name__ == '__main__':
	main(sys.argv)
		
